package org.su.generalmvc.scanner;

import com.google.protobuf.GeneratedMessage;
import com.google.protobuf.GeneratedMessageLite;
import org.su.generalmvc.IEditor;
import org.su.generalmvc.IParamBinder;
import org.su.generalmvc.annotaion.Skip;
import org.su.generalmvc.core.IXsshApplicationContext;
import org.su.generalmvc.core.InitialContext;
import org.su.generalmvc.entity.PojoCache;
import org.su.generalmvc.entity.ProtobufCache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;
import java.util.Set;


/**
 * 将复合类型的属性已编辑器的方式缓存起来，为后期参数注入提高速度
 * @author yangchuan createtime:2012-4-16下午4:48:38
 *
 */
public final class PojoWriteMehtodScanner {
    protected static final Logger logger = LoggerFactory.getLogger(PojoWriteMehtodScanner.class);

    private static final Class lbyte = (new byte[0]).getClass();

    /**
     * 当前bean 的类树,只有在里面出现个一次类,第二次就不再扫描
     */
    private static final ArrayList<Class<?>> classStatck = new ArrayList<>();

    //TODO 这里处理不科学，应该将protobuf的相关处理作为另一个分类，不能像现在这样耦合在一起，待优化。
	public static PojoCache scanMthod(Class<?> pojoType) {
        try {
            return _scanMthod(pojoType);
        }finally {
            classStatck.clear();
        }
    }

    public static PojoCache _scanMthod(Class<?> pojoType) {
        classStatck.add(pojoType);
        String pojoTypeName = pojoType.getName();
        Map<String, PojoCache> pojoCaches = InitialContext.getInstance().getContext().getPojoContext();
        PojoCache cache = pojoCaches.get(pojoTypeName);
        if(cache!=null){
            return cache;
        }

        Class<?> beanType = pojoType;
        Method build = null, newBuilder = null, parseFrom = null;
        if(GeneratedMessage.class.isAssignableFrom(beanType)||GeneratedMessageLite.class.isAssignableFrom(beanType)){
            try {
                newBuilder = beanType.getMethod("newBuilder");
                parseFrom = beanType.getMethod("parseFrom",lbyte);
                beanType = Class.forName(beanType.getName() + "$Builder");
                build = beanType.getMethod("build");
                cache = new ProtobufCache(build,newBuilder,parseFrom);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            }
        }else{
			cache = new PojoCache();
		}

        pojoCaches.put(pojoTypeName,cache);

		BeanInfo beanInfo = null;
	    try {
	      beanInfo = Introspector.getBeanInfo(beanType, Object.class);
	    } catch (IntrospectionException e) {
	    	throw new IllegalArgumentException("No bean class specified",e);
	    }
	    PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
		
		boolean flag = false;
        Method writer = null;
        String methodName = null;
        StringBuilder sb = null;
		for (PropertyDescriptor pd : pds) {
            /*if(pd.getPropertyType().equals(beanType)){
                continue;
            }*/


            writer = pd.getWriteMethod();
			if (writer == null && pd.getReadMethod() != null) {
                methodName = pd.getName();
                sb = new StringBuilder();
                sb.append("set");
                char[] cs = methodName.toCharArray();
                cs[0] -= 32;
                sb.append(cs);
                try {
                    writer = beanType.getMethod(sb.toString(),pd.getPropertyType());
                    sb = null;
                } catch (NoSuchMethodException e) {
                    logger.warn("{} 的‘{}’属性没有set方法",pojoTypeName,pd.getName());
                }

			}
            if(writer != null ) {
                IEditor editor = InitialContext.getInstance().getContext().getParamBinder(/*  DataBinder.getInstence()*/).findEditor(
                        pd.getPropertyType());

                cache.getEditors().add(editor);
                cache.getMethodNames().add(pd.getName());
                cache.getMethods().add(writer);
                cache.getReadMethods().add(pd.getReadMethod());
                flag = true;
                drillDown(editor, pd.getPropertyType(), writer);
            }
            writer = null;
            methodName = null;
            build = null;
            newBuilder = null;
		}
		if (flag) {
			pojoCaches.put(pojoTypeName, cache);
			return cache;
		} else {
			return null;
		}

	}

    /**
     * 下钻到类中的复杂类型,scan一次该类型,将该类型的属性分析出来
     * @param editor
     * @param type
     */
	private static void drillDown(IEditor editor,Class<?> type,Method method){
        IXsshApplicationContext context = InitialContext.getInstance().getContext();
        IParamBinder paramBinder = context.getParamBinder();
        if (paramBinder.isCustomEditor(editor)) {
            Class<?> temp = type;
            if(Collection.class.isAssignableFrom(temp)){
                Type fieldType = method.getGenericParameterTypes()[0];
                fieldType = ((ParameterizedType)fieldType).getActualTypeArguments()[0];
                temp = (Class)fieldType;
            }else if(temp.isArray()){
                String cname = temp.getCanonicalName();
                cname = cname.substring(0,cname.length()-2);
                try {
                    temp = Class.forName(cname);
                } catch (ClassNotFoundException e) {
                    logger.error("handle type {} faild",temp.getName(),e);
                }
            }

            if(paramBinder.isBaseEditor(temp) ||
                    classStatck.contains(temp)||
                    context.getPojoContext().containsKey(temp.getName())) {
                return;
            }
            PojoWriteMehtodScanner._scanMthod(temp);
        }
    }
}
